﻿'PDF 1.5 introduces a new kind of stream, an object stream, which contains a sequence of PDF objects. 
'The purpose of object streams is to allow a greater number of PDF objects to be compressed, thereby substantially reducing the size of PDF files. 
'The objects in the stream are referred to as compressed objects. 
Imports System.Text.Encoding
Friend Class clsPDFObjectStream
    Private _dictionnary As clsPdfDictionnary
    Private _n As Long  'The number of compressed objects in the stream.
    Private _first As Integer 'The byte offset (in the decoded stream) of the first compressed object.
    Private _streamObject As clsPdfStreamObject
    Private _buffer As Byte()   'the decoded stream
    Private _objectNumbers As New Dictionary(Of Long, _objectInfo)  'The list object numbers contains in the stream with their offsets
    Private _reader As clsPDFBaseReader 'The reader for the decoded stream
    Private Structure _objectInfo
        Dim objNumber As Long
        Dim offset As Integer
        Dim size As Integer
    End Structure

    Friend Sub New(obj As clsPdfBaseObject)
        If obj.Type = PdfObjectTypes.IndirectObject Then
            obj.ParseChildObjects()
            _dictionnary = obj.child(0).GetPdfDictionnary
            If Not _dictionnary.Item(PDF_TYPE).GetString = PDF_OBJSTM Then
                Throw New Exception("Cannot create clsPDFObjectStream")
            End If
            _n = _dictionnary.Item(PDF_N).GetLong
            _first = _dictionnary.Item(PDF_FIRST).GetLong
            _streamObject = New clsPdfStreamObject(_dictionnary, obj.child(1))
            _buffer = _streamObject.GetDecodedData
            _reader = New clsPDFBaseReader(_buffer)
            _readObjectNumbers()
        Else
            Throw New Exception("Cannot create clsPDFObjectStream")
        End If

    End Sub
    Friend Function GetObject(objNumber As Long) As clsPdfBaseObject
        Dim i As Long
        Dim indirectObj As clsPdfBaseObject
        Dim info As _objectInfo
        GetObject = Nothing
        If _objectNumbers.ContainsKey(objNumber) Then
            indirectObj = CreateIndirectObject(objNumber, 0)
            info = _objectNumbers.Item(objNumber)
            _reader.ReaderPosition = info.offset + _first

            For i = 1 To info.size
                indirectObj.AddValue(_reader.GetByte)
            Next

            GetObject = indirectObj
        End If
    End Function
    Private Sub _readObjectNumbers()
        Dim obj As clsPdfBaseObject
        Dim objNumber As Long
        Dim offset, lastOffset As Integer
        Dim count As Integer
        Dim info As _objectInfo
        Dim numbers As New List(Of _objectInfo)


        _reader.ReaderPosition = 0
        count = 0
        lastOffset = 0
        While count <> _n
            obj = _reader.GetObject()
            objNumber = obj.GetLong
            obj = _reader.GetObject()
            offset = obj.GetLong


            info = New _objectInfo
            info.offset = offset
            info.objNumber = objNumber

            numbers.Add(info)
            If offset > 0 Then
                info = numbers(count - 1)
                info.size = offset - lastOffset
                numbers(count - 1) = info
            End If
            count += 1
            lastOffset = offset
        End While
        info = numbers(count - 1)
        info.size = _buffer.Length - _first - lastOffset
        numbers(count - 1) = info

        For Each info In numbers
            _objectNumbers.Add(info.objNumber, info)
        Next
    End Sub
End Class
